
#include <ttg/all.h>

using namespace ttg::services;
using namespace ttg::pipeline;

//Defining type for storing Fibonacci numbers (that is large enough).
typedef unsigned __int64 QWORD;

//Serial recursive function for Fibonacci number calculation.
QWORD calculateFibonacciNumber(QWORD n)
{
	if (n <= 2)
		return 1;
	else
		return calculateFibonacciNumber(n - 1) + calculateFibonacciNumber(n - 2);
}

//Splits incoming number as sum of two another numbers.
class FibonacciSplitter :public Node<QWORD, QWORD>
{
	public:
		virtual void process(QWORD data)
		{
			//If it small enough, calculating serially and sending to next node.
			if (data <= 30)
				sendNext(calculateFibonacciNumber(data));
			//Otherwise, splitting into sum and sending to this node.
			else
			{
				sendThis(data - 1);
				sendThis(data - 2);
			}
		}
};

//Sums all incoming values and sends result after iteration end event.
class FibonacciSummator :public Node<QWORD, QWORD>
{
	private:
		QWORD res;
	public:
		//Clears result variable.
		//Will be performed before data processing.
		virtual void onBegin()
		{ res = 0; }
		//Sums incoming numbers.
		virtual void process(QWORD data)
		{ res += data; }
		//Sends result to next node (in our case it is user).
		//Will be called after data processing.
		virtual void onEnd()
		{ sendNext(res); }
		//Default constructor.
		//Note: "true" flags means, that this node will work serially.
		FibonacciSummator()
			:Node("FibonacciSummator", true)
		{ /*nothing*/ }
};

int main()
{
	//Initing services.
	ServiceBase::getRef()->start();
	//Creating pipeline as concatination of previously defined nodes.
	Pipeline<QWORD, QWORD> pl(wrap(new FibonacciSplitter()) + wrap(new FibonacciSummator()));
	//Starting pipeline and storing times.
	double t_start = Timer::getRef()->getSeconds();
	QWORD res = pl.run(45)[0];
	double t_end = Timer::getRef()->getSeconds();
	printf("Parallel calculating: result = %lld, elapsed time = %f seconds\n", res, t_end - t_start);
	//Calculating serially (for comparision).
	t_start = ServiceBase::_Timer().getSeconds();
	res = calculateFibonacciNumber(45);
	t_end = ServiceBase::_Timer().getSeconds();
	printf("Serial calculating: result = %lld, elapsed time = %f seconds\n", res, t_end - t_start);
	exit(0);
}
